package orion.jarvis.task;

import java.util.List;

import orion.jarvis.task.event.TaskEvent;
import orion.jarvis.task.event.TaskEventCategory;
import orion.jarvis.task.event.EventData;
import orion.jarvis.task.event.EventProvider;
import orion.jarvis.task.event.EventListenerQueue;
import orion.jarvis.task.exception.TaskServiceNotFoundException;


public class TaskManager implements EventProvider {

	private EventListenerQueue listenerQueue;

	private TaskService service;

	public TaskManager() {
		super();
	}

	private void checkService() throws TaskServiceNotFoundException {
		if (service == null)
			throw new TaskServiceNotFoundException();
	}

	public Message createTask(Task task) throws TaskServiceNotFoundException {
		beforeCreateTask(task);
		checkService();
		Message msg = service.createTask(task);
		afterCreateTask(task, msg);
		return msg;
	}

	private void beforeCreateTask(Task task) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_CREATE;
			TaskEvent event = provide(category, task);
			listenerQueue.perform(event);
		}
	}

	private void afterCreateTask(Task task, Message msg) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_CREATE;
			TaskEvent event = provide(category, task, msg);
			listenerQueue.perform(event);
		}
	}

	public Message updateTask(Task task) throws TaskServiceNotFoundException {
		beforeUpdateTask(task);
		checkService();
		Message msg = service.updateTask(task);
		afterUpdateTask(task, msg);
		return msg;
	}

	private void beforeUpdateTask(Task task) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_UPDATE;
			TaskEvent event = provide(category, task);
			listenerQueue.perform(event);
		}
	}

	private void afterUpdateTask(Task task, Message msg) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_UPDATE;
			TaskEvent event = provide(category, task, msg);
			listenerQueue.perform(event);
		}
	}

	public Message finishTask(Task task) throws TaskServiceNotFoundException {
		beforeFinishTask(task);
		checkService();
		Message msg = service.finishTask(task);
		afterFinishTask(task, msg);
		return msg;
	}

	private void beforeFinishTask(Task task) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_FINISH;
			TaskEvent event = provide(category, task);
			listenerQueue.perform(event);
		}
	}

	private void afterFinishTask(Task task, Message msg) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_FINISH;
			TaskEvent event = provide(category, task, msg);
			listenerQueue.perform(event);
		}
	}

	public Message closeTask(Task task) throws TaskServiceNotFoundException {
		beforeCloseTask(task);
		checkService();
		Message msg = service.closeTask(task);
		afterCloseTask(task, msg);
		return msg;
	}

	private void beforeCloseTask(Task task) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_CLOSE;
			TaskEvent event = provide(category, task);
			listenerQueue.perform(event);
		}
	}

	private void afterCloseTask(Task task, Message msg) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_CLOSE;
			TaskEvent event = provide(category, task, msg);
			listenerQueue.perform(event);
		}
	}

	public List<Task> findTask(Condition condition) throws TaskServiceNotFoundException {
		beforeFindTask(condition);
		checkService();
		List<Task> list = service.findTask(condition);
		afterFindTask(condition, list);
		return list;
	}

	private void afterFindTask(Condition cond, List<Task> list) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_FIND;
			TaskEvent event = provide(category, cond, list);
			listenerQueue.perform(event);
		}
	}

	private void beforeFindTask(Condition cond) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_FIND;
			TaskEvent event = provide(category, cond);
			listenerQueue.perform(event);
		}
	}

	public Task getTask(Integer id) throws TaskServiceNotFoundException {
		beforeGetTask(id);
		checkService();
		Task task = service.getTask(id);
		afterGetTask(id, task);
		return task;
	}

	private void beforeGetTask(Integer id) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.BEFORE_GET;
			TaskEvent event = provide(category, id);
			listenerQueue.perform(event);
		}
	}

	private void afterGetTask(Integer id, Task task) {
		if (listenerQueue != null) {
			TaskEventCategory category = TaskEventCategory.AFTER_GET;
			TaskEvent event = provide(category, id, task);
			listenerQueue.perform(event);
		}
	}

	public void setListenerQueue(EventListenerQueue listenerQueue) {
		this.listenerQueue = listenerQueue;
	}

	public EventListenerQueue getListenerQueue() {
		return listenerQueue;
	}

	public TaskService getService() {
		return service;
	}

	public void setService(TaskService service) {
		this.service = service;
	}

	public TaskEvent provide(TaskEventCategory category, Task task) {
		return provide(category, null, task);
	}

	public TaskEvent provide(TaskEventCategory category, Integer id) {
		return provide(category, id, null);
	}

	public TaskEvent provide(TaskEventCategory category, Integer id, Task task) {
		EventData data = new EventData();
		data.setTaskId(id);
		data.setTask(task);
		TaskEvent event = new TaskEvent();
		event.setCategory(category);
		event.setEventData(data);
		return event;
	}

	public TaskEvent provide(TaskEventCategory category, Condition cond) {
		return provide(category, cond, null);
	}

	public TaskEvent provide(TaskEventCategory category, Condition cond, List<Task> list) {
		EventData data = new EventData();
		data.setCondition(cond);
		data.setTaskList(list);
		TaskEvent event = new TaskEvent();
		event.setCategory(category);
		event.setEventData(data);
		return event;
	}

	public TaskEvent provide(TaskEventCategory category, Task task, Message msg) {
		return provide(category, task, msg);
	}

}